package han.Chensing.CCirnosLibrary.util;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import han.Chensing.CCirnosLibrary.P;

public class AESUtil {

    public static byte[] iv="CCirnosLibraryIV".getBytes(StandardCharsets.UTF_8);

    public static CipherInputStream wrapInputStream(InputStream inputStream,String key)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        byte[] keySpec = paddingKey(key);
        SecretKeySpec secretKeySpec=new SecretKeySpec(keySpec,"AES");
        Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE,secretKeySpec,ivParameterSpec);
        return new CipherInputStream(inputStream,cipher);
    }

    public static CipherOutputStream wrapOutputStream(OutputStream outputStream, String key)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        byte[] keySpec = paddingKey(key);
        SecretKeySpec secretKeySpec=new SecretKeySpec(keySpec,"AES");
        Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec,ivParameterSpec);
        return new CipherOutputStream(outputStream,cipher);
    }

    public static byte[] encryptDirect(byte[] input,String key)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
            BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        byte[] keySpec = paddingKey(key);
        SecretKeySpec secretKeySpec=new SecretKeySpec(keySpec,"AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,ivParameterSpec);
        return cipher.doFinal(input);
    }

    public static byte[] decryptDirect(byte[] input,String key)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
            BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        byte[] keySpec = paddingKey(key);
        SecretKeySpec secretKeySpec=new SecretKeySpec(keySpec,"AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,ivParameterSpec);
        return cipher.doFinal(input);
    }

    public static byte[] paddingKey(String key){
        byte[] bytes = key.getBytes(StandardCharsets.UTF_8);
        int length = bytes.length;
        int nLength;
        if (length>0&&length<=16)
            nLength=16;
        else if (length>16&&length<=24)
            nLength=24;
        else if (length>24&&length<=32)
            nLength=32;
        else
            throw new NullPointerException();
        byte[] nBytes=new byte[nLength];
        byte[] mask=length%2==0?
                new byte[]{114,-51,-41,91,98,-10}:
                new byte[]{-1,5,41,-91,-8,-91,0};
        System.arraycopy(bytes, 0, nBytes, 0, bytes.length);
        for(int i=0,j=0;i<nBytes.length;i++,j++){
            if (nBytes[i]==0)
                nBytes[i]=mask[j];
            else
                nBytes[i]&=mask[j];
            if (j>=mask.length-1)
                j=-1;
        }
        P.i("PdK:"+bytesToVisibleString(nBytes));
        return nBytes;
    }

    public static String bytesToVisibleString(byte[] bytes){
        StringBuilder stringBuilder=new StringBuilder();
        for (byte aByte : bytes) {
            if (-128< (int) aByte && (int) aByte <=-96)
                stringBuilder.append("1");
            else if (-96< (int) aByte && (int) aByte <=-64)
                stringBuilder.append("2");
            else if (-64< (int) aByte && (int) aByte <=-32)
                stringBuilder.append("3");
            else if (-32< (int) aByte && (int) aByte <=0)
                stringBuilder.append("4");
            else if (0< (int) aByte && (int) aByte <=32)
                stringBuilder.append("a");
            else if (32< (int) aByte && (int) aByte <=64)
                stringBuilder.append("b");
            else if (64< (int) aByte && (int) aByte <=96)
                stringBuilder.append("c");
            else if (96< (int) aByte && (int) aByte <=127)
                stringBuilder.append("d");
        }
        return stringBuilder.toString();
    }

    public static String b2s(byte[] bytes){
        StringBuilder builder=new StringBuilder();
        for(int i=0;i<bytes.length;i++) {
            String str=Integer.toHexString(0xFF&(int)bytes[i]);
            if (str.length()==1)
                builder.append("0");
            builder.append(str.toUpperCase());
            if (i==bytes.length-1)
                continue;
            builder.append(" ");
        }
        return builder.toString();
    }
}
